<?php
/**
 * Copyright (c) 2006-2007, Julien PORTALIER
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright    Copyright (c) 2006-2007, Julien PORTALIER
 * @link         http://featherphp.googlecode.com/
 * @license      http://www.opensource.org/licenses/mit-license.php The MIT License
 */

define('ORIG_INCLUDE_PATH', ini_get('include_path'));
ini_set('include_path', ORIG_INCLUDE_PATH.':'.APP.':'.FEATHER.':'.dirname(FEATHER));

/**
 * Automatic class loading.
 */
function __autoload($class)
{
	if (preg_match('/(Controller|Model|Component|Behavior|Helper)$/', $class, &$match))
	{
		$file = Inflector::underscore(str_replace($match[1], '', $class));
		$path = Inflector::tableize($match[1]);
		
		if ($path == 'components')
			$path = 'controllers'.DS.$path;
		elseif ($path == 'components')
			$path = 'behaviors'.DS.$path;
	}
	else
	{
		$file = Inflector::underscore($class);
		$path = 'libs';
	}
	Load::_load($path, $file);
}

/**
 * Framework's basic class loader.
 */
class Load
{
	static private $instance;
	static public  $plugin;
	
	private $_models;
	public  $controller;
	public  $view;
	
	# config
	
	private function __construct() {}
	static  function getInstance()
	{
		if (!self::$instance)
			self::$instance = new self();
		return self::$instance;
	}
	
	static function setPlugin($plugin)
	{
		ini_set('include_path', ORIG_INCLUDE_PATH.':'.
			APP.'plugins'.DS.$plugin.':'.APP.':'.FEATHER.':'.dirname(FEATHER));
		self::$plugin = $plugin;
		
		if (!defined('PLUGIN_DB_PREFIX'))
			define('PLUGIN_DB_PREFIX', self::$plugin.'_');
	}
	
	# static methods
	
	static function _load($path, $files)
	{
		if (!is_array($files))
		{
			$files = func_get_args();
			unset($files[0]);
		}
		foreach($files as $name)
		{
			if (!include_once $path.DS."$name.php")
			{
				if (DEBUG)
					trigger_error("Unable to load file $path".DS."$name.php", E_USER_ERROR);
				Error::show(500);
			}
		}
	}
	
	static function libs()
	{
		$args = func_get_args();
		return self::_load('libs', &$args);
	}
	
	static function vendors()
	{
		$files = func_get_args();
		foreach($files as $name)
		{
			if (!include_once 'vendors'.DS.$name)
			{
				if (DEBUG)
					trigger_error("Unable to load file $path".DS."$name", E_USER_ERROR);
				Error::show(500);
			}
		}
	}
	
	# magic methods
	
	function __call($func, $args=null)
	{
		if ($func == 'helper')
		{
			$this->helpers($args[0]);
			return isset($this->view) ? $this->view->get($args[0]) : null;
		}
		else
		{
			$func = "{$func}s";
			if (method_exists($this, $func))
				return $this->$func($args[0]);
			trigger_error("There is no such method 'Load::$func()'.", E_USER_ERROR);
		}
	}
	
	# public methods
	
	function controller($class, $url, $action, &$params)
	{
		self::_load('controllers', 'controller', 'app_controller', $class);
		
		$class = Inflector::camelize($class).'Controller';
		$controller = new $class($url, $action, $params);
		$controller->load = self::$instance;
		$controller->load->auto($controller);
		
		return $controller;
	}
	
	function script($path, $action)
	{
		self::_load('controllers', 'controller', 'script');
		
		if (!file_exists(SCRIPTS.$path.'.php'))
			die("Error: couldn't find $path.php\n");
		require_once SCRIPTS.$path.'.php';
		
		$class  = Inflector::camelize(basename($path)).'Script';
		$script = new $class($action);
		$script->load = self::$instance;
		$this->controller = $script;
		
		if ($script->database)
		{
			self::_load('database', 'database');
			self::_load('models',   array('model', 'active_record'));
		}
		
		return $script;
	}
	
	function auto(Controller $controller)
	{
		$this->controller = $controller;
		
		if (Core::database)
		{
			self::_load('database', 'database');
			self::_load('models',   array('model', 'active_record'));
		}
		
		# libs
		if (!empty(Core::$libs))
			self::_load('libs', Core::$libs);
		
		# components
		$components = array_merge(Core::$components, $controller->components);
		if (!empty($components))
			call_user_func_array(array($this, 'components'), &$components);
		
		# start
		foreach($controller->beforeFilter as $func)
			$controller->$func();
	}
	
	function components()
	{
		$args = func_get_args();
		foreach($args as $c)
		{
			if (!isset($this->controller->$c))
			{
				// load
				self::_load('controllers'.DS.'components', Inflector::Underscore($c));
				$class = "{$c}Component";
				
				// create
				$obj = new $class;
				$this->controller->$c = $obj;
				
				// start up
				if (method_exists($obj, 'startup') and $this->controller instanceof Controller)
					$obj->startup($this->controller);
				
				// bind callbacks
				if (isset($obj->bindTo))
				{
					foreach($obj->bindTo as $action => $funcs)
					{
						if (is_string($funcs))
							$funcs = explode(',', $funcs);
						foreach($funcs as $f)
							$this->controller->bindCallback($action, $c, trim($f));
					}
				}
			}
			else {
				trigger_error(get_class($this->controller)."::$c has already been declared.", E_USER_WARNING);
			}
		}
	}
	
	function model($class)
	{
		if (!isset($this->_models[$class]))
		{
			self::_load('models', Inflector::underscore($class));
			$this->_models[$class] = new $class($this->controller);
		}
		return $this->_models[$class];
	}
	
	function behavior($class, Model $model)
	{
		if (!isset($model->$class))
		{
			self::_load('models'.DS.'behaviors', Inflector::Underscore($class));
			$c = "{$class}Behavior";
			$model->$class = new $c($model);
		}
		else {
			trigger_error(get_class($model)."::$class has already been declared.", E_USER_WARNING);
		}
	}
	
	function helpers()
	{
		$args = func_get_args();
		Core::$helpers = array_merge(Core::$helpers, $args);
		
		if (isset($this->view))
		{
			foreach(Core::$helpers as $h)
			{
				self::_load('views', 'helper');
				self::_load('views'.DS.'helpers', Inflector::Underscore($h));
				$class = Inflector::Camelize($h).'Helper';
				
				// create
				$obj = new $class($this->view);
				$this->view->set($h, $obj);
				
				// callbacks
				if (isset($obj->bindTo))
				{
					foreach($obj->bindTo as $action => $funcs)
					{
						if (is_string($funcs))
							$funcs = explode(',', $funcs);
						foreach($funcs as $f)
							$this->view->bindCallback($action, $c, trim($f));
					}
				}
			}
		}
	}
}
?>